ULedger Virtual Machine
The ULedger Virtual Machine (ULVM) allows you to write smart contracts. Contracts are compiled to WebAssembly (WASM), and the ULVM exposes a small set of host functionn your contract can call to:
- read/write blockchain state (key–value storage)
- log messages
- emit events
- access contract context (caller, owner, block height)
State model: key–value storage
ULedger stores contract state as a key–value map in the Blockchain. Your contract reads and writes values by key.
Conceptually, the contract state looks like:
{
"state": {
"key1": "value1",
"key2": "value2"
}
}
So when you call set_storage("my-key", someValue).
Supported data types
Because state is serialized, the ULVM supports a fixed set of types (via the serializer that encodes/decodes values):
Null = 0,
Bool = 1,
Int32 = 2,
Int64 = 3,
String = 4,
Bytes = 5,
Array = 6,
Map = 7,
Your helper methods (like
storeInt,retrieveInt, etc.) use this serializer internally so you don’t have to manually encode/decode bytes.
ULVM host functions (imports)
In AssemblyScript, the ULVM host functions are (high level):
get_storage: fetch value bytes for a keyset_storage: store value bytes under a keydelete_storage: remove a key from statehas_key: check if a key existslog: write a log messageemit_event: emit an event with a name + data payloadget_caller: the account/wallet that is calling this contractget_owner: the contract ownerget_block_height: current block height
These functions take pointers (
usize) because WASM passes data through linear memory. In practice, you’ll usually call wrapper utilities (likestoreInt) that handle pointers + serialization for you.
Why strong typing (AssemblyScript)
Your contracts are written in AssemblyScript (TypeScript-like syntax compiled to WASM). Because it compiles to WebAssembly, you’ll use explicit types like i32, i64, bool, Array<i32>, etc.
Tutorial: Store and retrieve (basic example)
This first example shows the simplest flow:
- store an
i32under a key - retrieve it back
Step 1: import the ULVM helpers
import {
storeInt,
retrieveInt,
storeIntArray,
retrieveIntArray,
} from "../src/ulvm";
These helpers are thin wrappers around env.get_storage / env.set_storage, plus serialization.
Step 2: write the contract
// The entry file of your WebAssembly module.
import {
storeInt,
retrieveInt,
storeIntArray,
retrieveIntArray,
} from "../src/ulvm";
/**
* Simple direct test function that stores a value and retrieves it.
* Uses a fixed key to keep the example minimal.
*/
export function testStoreAndRetrieve(value: i32): i32 {
const key = "test-key";
// Store the value
storeValue(key, value);
// Retrieve and return the value
return retrieveValue(key);
}
/**
* Store a value in storage with a string key
*/
export function storeValue(key: string, value: i32): void {
storeInt(key, value);
}
/**
* Retrieve a value from storage by string key
*/
export function retrieveValue(key: string): i32 {
return retrieveInt(key);
}
/**
* Test storing and retrieving an array
*/
export function testStoreAndRetrieveArray(): i32 {
const key = "array-key";
const array = [10, 20, 30, 40, 50];
storeArray(key, array);
const retrieved = retrieveArray(key);
// Return the length to verify it worked
return retrieved.length;
}
/**
* Store an array with a string key
*/
export function storeArray(key: string, values: Array<i32>): void {
storeIntArray(key, values);
}
/**
* Retrieve an array by string key
*/
export function retrieveArray(key: string): Array<i32> {
return retrieveIntArray(key);
}
What this contract does
testStoreAndRetrieve(value: i32) -> i32
- writes
valueinto storage under"test-key" - reads
"test-key"back - returns the retrieved value
So if you call testStoreAndRetrieve(123), your state ends up conceptually like:
{
"state": {
"test-key": 123
}
}
testStoreAndRetrieveArray() -> i32
- stores
[10,20,30,40,50]under"array-key" - retrieves it back
- returns the array length (
5)
Build: compile AssemblyScript to WebAssembly
If you’re using the standard AssemblyScript project layout, the typical flow is:
1) Install AssemblyScript (dev dependency)
npm i -D assemblyscript
2) Put your contract entry in assembly/index.ts
For example:
assembly/index.ts(your contract file)
3) Compile to .wasm
Option A: direct asc command
npx asc assembly/index.ts \
--target release \
--outFile build/contract.wasm
Option B: if your repo already has asbuild
Many AssemblyScript setups include scripts like:
npm run asbuild:release
Result: you’ll have a .wasm file (example: build/contract.wasm) that the ULVM can execute.
Run: Deploy
In order to deploy the compiled Smart Contract you can use the ULedger SDK.
For the Go guide on how to do this please refer to the Deploying Smart Contracts documentation.